package com.tbit.uqbike.client.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.tbit.uqbike.client.constant.AccountConfigExtConstant;
import com.tbit.uqbike.client.constant.MachineStateConstant;
import com.tbit.uqbike.client.dao.core.UserDetailDao;
import com.tbit.uqbike.client.pojo.MachineFunction;
import com.tbit.uqbike.client.pojo.UserDetail;
import com.tbit.uqbike.client.pojo.dto.UserDetailUpdateDTO;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.vo.UserWeightLogQueryReqVO;
import com.tbit.uqbike.client.pojo.vo.UserWeightLogRespVO;
import com.tbit.uqbike.client.service.CacheService;
import com.tbit.uqbike.client.service.MachineFunctionService;
import com.tbit.uqbike.client.service.UserDetailService;
import com.tbit.uqbike.client.service.UserWeightLogService;
import com.tbit.uqbike.client.util.DateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:li.yao@tbit.com.cn">yao</a>
 * @version 4.2.4
 * @since 2024/12/30 15:34
 */
@Slf4j
@Service
public class UserDetailServiceImpl implements UserDetailService {

    @Resource
    private UserDetailDao userDetailDao;

    @Resource
    private CacheService cacheService;

    @Resource
    private MachineFunctionService machineFunctionService;

    @Resource
    private UserWeightLogService userWeightLogService;

    @Override
    public void updateByUserId(UserDetail userDetail) {
        userDetailDao.updateByUserId(userDetail);
    }

    @Override
    public void create(UserDetail userDetail) {
        userDetailDao.insert(userDetail);
    }

    @Override
    public UserDetail get(Integer type, Integer userId) {
        Map<String, Object> params = new HashMap<>();
        params.put("type", type);
        params.put("userId", userId);
        return userDetailDao.select(params);
    }

    @Override
    public boolean shouldLogWeight(Integer accountId, Integer machineId) {
        // 区域体重配置
        AccountConfigExt cyclingOverloadMonitoringExt = cacheService.selectByAccountIdAndKey(accountId, AccountConfigExtConstant.CYCLING_OVERLOAD_MONITORING);
        // 车辆体重配置
        MachineFunction machineWeightFunction;

        return cyclingOverloadMonitoringExt != null
                && "1".equals(cyclingOverloadMonitoringExt.getParamValue())
                && (machineWeightFunction = machineFunctionService.get(machineId, MachineStateConstant.MACHINE_TYPE_WEIGHT)) != null
                && Objects.equals(machineWeightFunction.getFunctionValue(), 1)
                && this.checkOverWeightTime(accountId);
    }

    @Override
    public void updateWeight(UserDetailUpdateDTO userDetailUpdate) {
        UserDetail userDetail = get(0, userDetailUpdate.getUserId());

        Date createTime = Optional.ofNullable(userDetail).map(UserDetail::getCreateTime).orElse(null);

        UserWeightLogQueryReqVO queryReqVO = UserWeightLogQueryReqVO.builder()
                .size(20)
                .status(1)
                .userId(userDetailUpdate.getUserId())
                .startTime(createTime).build();

        List<UserWeightLogRespVO> userWeightLogs = userWeightLogService.getListRange(queryReqVO);

        UserWeightLogRespVO weight = userWeightLogs.stream().filter(userWeightLog -> userWeightLog.getOrderNo().equals(userDetailUpdate.getOrderNo())).findAny().orElse(null);

        if (weight == null) {
            log.info("[updateWeight weight is null][order: {}]", userDetailUpdate.getOrderNo());
            return;
        }

        if (CollUtil.isNotEmpty(userWeightLogs) && userWeightLogs.size() > 5) {
            // 排序后去掉最大值和最小值
            userWeightLogs = userWeightLogs.stream()
                    // 排序
                    .sorted(Comparator.comparingInt(UserWeightLogRespVO::getWeight))
                    // 跳过第一个元素（最小值）
                    .skip(1)
                    // 限制元素数（去掉最后一个元素，即最大值）
                    .limit(userWeightLogs.size() - 2)
                    .collect(Collectors.toList());
        }

        // 90 天
        // long expireSeconds = 24 * 60 * 60 * 90;
        long expiredSeconds = 7776000;

        // 历史体重有效百分比
        Integer historicalEffectiveWeightRange = Optional.ofNullable(cacheService.getAccountConfigExtByKey(userDetailUpdate.getAccountId(), AccountConfigExtConstant.HISTORICAL_EFFECTIVE_WEIGHT_RANGE))
                .map(AccountConfigExt::getParamValue)
                .map(Integer::valueOf)
                .orElse(30);

        double percentOfWeight = weight.getWeight() * historicalEffectiveWeightRange / 100.0;

        double startWeightRange = weight.getWeight() - percentOfWeight;
        double endWeightRange = weight.getWeight() + percentOfWeight;

        // 排除超过 90 天的，并且范围不在 30%
        Date now = new Date();
        userWeightLogs = userWeightLogs.stream().filter(userWeightLog -> {
            Date createLocalDateTime = DateUtil.parse(userWeightLog.getCreateTime(), DatePattern.NORM_DATETIME_MS_PATTERN);
            long betweenSeconds = DateUtil.between(now, createLocalDateTime, DateUnit.SECOND);
            return betweenSeconds <= expiredSeconds
                    || (userWeightLog.getWeight() >= startWeightRange && userWeightLog.getWeight() < endWeightRange);
        }).collect(Collectors.toList());

        // 计算平均值（四舍五入）
        long weightAverage = Math.round(userWeightLogs.stream().mapToInt(UserWeightLogRespVO::getWeight).average().orElse(weight.getWeight()));

        // 更新用户体重
        updateUserWeight(userDetail, userDetailUpdate, weightAverage, weight.getCreateTime());
    }

    /**
     * @description: 更新或创建用户体重信息
     * @author: mo.shanyong
     * @date: 2025/4/11 15:09
     * @param: userDetail
     * @param: userDetailUpdate
     * @param: weightAverage
     * @param: createTime
     */
    private void updateUserWeight(UserDetail userDetail, UserDetailUpdateDTO userDetailUpdate, long weightAverage, String createTime) {
        UserDetail userDetailNew = new UserDetail();
        userDetailNew.setUserId(userDetailUpdate.getUserId());
        userDetailNew.setType(0);
        userDetailNew.setValue(String.valueOf(weightAverage));
        userDetailNew.setMachineNo(userDetailUpdate.getMachineNo());
        userDetailNew.setAccountId(userDetailUpdate.getAccountId());
        userDetailNew.setBrandId(userDetailUpdate.getBrandId());
        Date time = DateUtil.parse(createTime, DatePattern.NORM_DATETIME_MS_PATTERN);
        userDetailNew.setCreateTime(time);
        userDetailNew.setUpdateTime(time);
        if (userDetail == null || userDetail.getId() == null) {
            create(userDetailNew);
        } else {
            updateByUserId(userDetailNew);
        }
    }

    /**
     * @description: 检查当前时间是否在超重配置的时间内
     * @author: mo.shanyong
     * @date: 2025/4/11 15:11
     * @param: accountId
     * @return: boolean
     */
    private boolean checkOverWeightTime(Integer accountId){
        List<String> list = new ArrayList<>(Arrays.asList(AccountConfigExtConstant.OVER_WEIGHT_DATE, AccountConfigExtConstant.OVER_WEIGHT_TIME));
        //体重检测有效期配置
        List<AccountConfigExt> accountConfigExtList =
                cacheService.getAccountConfigExtByKeys(accountId,list);
        //如果没有配置日期时间段，则走之前的逻辑，直接返回true
        if (CollectionUtils.isEmpty(accountConfigExtList)){
            return true;
        }
        Map<String, String> map = accountConfigExtList.stream().collect(Collectors.toMap(AccountConfigExt::getParamKey, AccountConfigExt::getParamValue));
        LocalDateTime now = LocalDateTime.now();
        //检查当前日期是否在配置额日期内
        String date = map.get(AccountConfigExtConstant.OVER_WEIGHT_DATE);
        boolean dateCheck = true;
        //如果不存在配置的日期，则表示根据任何日期的指定时间段内检查
        if (StringUtils.isNotBlank(date)) {
            dateCheck = false;
            String[] split = date.split("~");
            LocalDate startDate = DateTimeUtil.getLocalDateByStr(split[0]);
            LocalDate endDate = DateTimeUtil.getLocalDateByStr(split[1]);
            if ((now.toLocalDate().isAfter(startDate) || now.toLocalDate().equals(startDate)) && (now.toLocalDate().isBefore(endDate) || now.toLocalDate().equals(endDate))) {
                dateCheck = true;
            }
        }
        //如果日期都不符合就直接返回false
        if (!dateCheck){
            return dateCheck;
        }
        //检查时间是否在指定的时间内，依赖于日期的检查结果，必须保证日期检查时正确的
        String time = map.get(AccountConfigExtConstant.OVER_WEIGHT_TIME);
        //如果没配置时间则代表任意时间内都符合
        if (StringUtils.isBlank(time)){
            return dateCheck;
        }
        String[] split = time.split(",");
        for (String s : split) {
            String[] timeArr = s.split("~");
            LocalTime startTime = DateTimeUtil.getLocalTimeByStr(timeArr[0]);
            LocalTime endTime = DateTimeUtil.getLocalTimeByStr(timeArr[1]);
            if ((now.toLocalTime().isAfter(startTime) || now.toLocalTime().equals(startTime))
                    && (now.toLocalTime().isBefore(endTime) || now.toLocalTime().equals(endTime))){
                return true;
            }
        }
        return false;
    }

}
